<?php 
class Dir {
	private $md5=array();
  	private static $_instance;
  	
  	private function __construct(){

  	}
  	public static function md5Dir($path){
		if(!is_dir($path)){
			PLoader::import('pisces.base.exceptions.BaseFileException');
			PLoader::import('pisces.base.exceptions.BaseFilePermissionException');
			throw new BaseFilePermissionException($pathname,4);
		}
		$dirObj = new RecursiveDirectoryIterator($path);
		
  	  	if(is_null(self::$_instance)){
  			self::$_instance = new self;
  		}
  		$result = self::$_instance->getFileList($dirObj);
		return self::$_instance->setDirMd5($result);
  	}
  	
    public static function mkDir($pPath, $pSystem="linux"){
        if($pSystem=="linux"){
            $paths = explode("/", $pPath);
            $temp = "";
            for($i=0;$i<count($paths);$i++)
            {
                $temp .= $paths[$i]."/";
                if(!is_dir($temp))
                {
                    mkdir($temp);
                }
            }
            return true;
        }
    }
    
	public static function creatMultPath($pathname){
		$checkPath = self::checkDir($pathname);
		if(empty($checkPath)){
			PLoader::import('pisces.base.exceptions.BaseFileException');
			PLoader::import('pisces.base.exceptions.BaseFilePermissionException');
			self::mkdir($pathname);
			$checkPath = self::checkDir($pathname);
			if(empty($checkPath)) throw new BaseFilePermissionException($pathname,2);
		}
		return $pathname;
	}
	
    /**
     * 删除整个目录和目录中的文件
     * @author 常伟
     * @createTime 2004-12-30
     * @param $pPath 目录
     * @param $pOption F:删除目录文件；R：删除文件和目录
     */
    function remove($pPath, $pOption=""){
        $pPath = StringTools::getValidPath($pPath);
        $files = self::getFiles($pPath);

        switch(strtoupper($pOption))
        {
            case "F":
                foreach($files as $file)
                    unlink($pPath.$file);
                return true;
            case "R":
                foreach($files as $file)
                    unlink($pPath.$file);
                return rmdir($pPath);
            default:
                if(count($files)>0)
                    return false;
                else
                    return rmdir($pPath);
        }
    }
	
    /**
     * 返回指定目录下的所有文件的列表,带路径
     * @param $pPath 路径
     * @return 返回输入路径下的所有文件列表
     */
    public static function getFiles($pPath){
        $pPath = StringTools::getValidPath($pPath);
        $list = self::scandir($pPath);

        $result = array();
        foreach($list as $file)
        {
            if(is_file($pPath.$file))
            {
                $result[] = $file;
            }
        }
        return $result;
    }

    /**
     * 返回指定目录下的所有目录的列表,带路径
     * @param $pPath 路径
     * @return 返回输入路径下的所有目录列表
     */
    public static function getDirectorys($pPath){
        $pPath = StringTools::getValidPath($pPath);
        $list = self::scandir($pPath);

        $result = array();
        foreach($list as $file)
        {
            if(is_dir($pPath.$file) && $file!="." && $file!="..")
            {
                $result[] = $file;
            }
        }
        return $result;
    }
    
    /**
     * 判断目录或者文件可写情况
     * @param string $file_path 目录路径
     * @return 000三位置分别代表可修改，可写,可读
     */
    public static function checkDir($file_path){
		/* 如果不存在，则不可读、不可写、不可改 */ 
		if (!file_exists($file_path)){  
			return false;  
		}  
		$mark = 0;  
		if (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN'){  
			/* 测试文件 */ 
			$test_file = $file_path . '/test.txt';
			
			/* 如果是目录 */ 
			if (is_dir($file_path)){  
				/* 检查目录是否可读 */ 
				$dir = @opendir($file_path);  
				if ($dir === false) {  
					return $mark; //如果目录打开失败，直接返回目录不可修改、不可写、不可读  
				}  
				if (@readdir($dir) !== false)  {  
					$mark ^= 1; //目录可读 001，目录不可读 000  
				} 
				@closedir($dir);  
				/* 检查目录是否可写 */ 
				$fp = @fopen($test_file, 'wb');
				if ($fp === false){  
					return $mark; //如果目录中的文件创建失败，返回不可写。  
				}
				if (@fwrite($fp, 'directory access testing.') !== false){  
					$mark ^= 2; //目录可写可读011，目录可写不可读 010  
				}  
				@fclose($fp);  
				@unlink($test_file);  
				/* 检查目录是否可修改 */ 
				$fp = @fopen($test_file, 'ab+');  
				if ($fp === false){  
					return $mark;  
				}  
				if (@fwrite($fp, "modify test.\r\n") !== false){  
					$mark ^= 4;  
				}  
				@fclose($fp);  
				/* 检查目录下是否有执行rename()函数的权限 */ 
				if (@rename($test_file, $test_file) !== false){  
					$mark ^= 8;  
				}  
				@unlink($test_file);  
			} elseif (is_file($file_path))  {  
				/* 如果是文件 */
				/* 以读方式打开 */ 
				$fp = @fopen($file_path, 'rb');  
				if ($fp){  
					$mark ^= 1; //可读 001  
				}  
				@fclose($fp);  
				/* 试着修改文件 */ 
				$fp = @fopen($file_path, 'ab+');  
				if ($fp && @fwrite($fp, '') !== false){  
				$mark ^= 6; //可修改可写可读 111，不可修改可写可读011...  
				}  
				@fclose($fp);  
				/* 检查目录下是否有执行rename()函数的权限 */ 
				if (@rename($test_file, $test_file) !== false){  
					$mark ^= 8;  
				}  
			}  
		}else{  
			if (@is_readable($file_path)){  
			$mark ^= 1;  
			}  
			if (@is_writable($file_path)){  
			$mark ^= 14;  
			}  
		}  
		return $mark;  
    }
 
    
   /** Example:
     * xCopy("feiy","feiy2",1):拷贝feiy下的文件到 feiy2,包括子目录
     * xCopy("feiy","feiy2",0):拷贝feiy下的文件到 feiy2,不包括子目录
     * @param $pSource 源目录名
     * @param $pTarget 目的目录名
     * @param $pInlcudeChild 复制时，是不是包含的子目录
     * @return 复制成功true 失败false
     */
   public static function xCopy($pSource, $pTarget, $pInlcudeChild=true)
    {
	    if(!is_dir($pSource))
	    {
		    echo("Error:$pSource 不是一个合法目录");
		    return false;
	    }
	    $all = self::scandir($pSource);
	    if(!is_dir($pTarget))
	    {
		    mkdir($pTarget);
	    }

	    foreach($all as $entry)
	    {
	        if(($entry!=".")&&($entry!=".."))
	        {
	            if(is_dir($pSource."/".$entry))
	            {
	                self::xCopy($pSource."/".$entry, $pTarget."/".$entry, $pInlcudeChild);
	            }
	            else
	            {
	                self::copy($pSource."/".$entry, $pTarget."/".$entry);
	            }
	        }
	    }

	    return true;
    }
    
    
    private function getFileList(RecursiveDirectoryIterator $newDir =null){
    	$result = array();
    	foreach($newDir as $value){
    		 $path_name = $value->getFilename();
    		if($path_name{0} == ".") continue;
    		 
    		if($value->isDir()){
    			$subDir = new RecursiveDirectoryIterator($value->getPathname());
    			$path_name = $value->getFilename();
    			$dir= $this->getFileList($subDir);
    			$dir['attr'] = array('name'=>$value->getPathname());
    			$result[$path_name]=$dir;
    		}
    		if($value->isFile()){
    			$result['files'][]=$this->getFile($value);
    		}
    	}
    	return $result;
    }
    private function getFile(SplFileInfo $file){
    	$file_tags = array();
    	$file_tags['attr']['filename'] = $file->getFilename();
    	$file_tags['path'] = $file->getPathname();
        $this->md5[$file->getPath()][]=$file_tags['attr']['md5']  = md5_file($file->getFileInfo());
        return $file_tags;
    }
    private function setDirMd5($array){
    	$dir_md5 = array();
        foreach($this->md5 as $key=>$value){
        	$dir_md5_list = array();
        	foreach($value as $files){
        		$dir_md5_list[] = $files['md5'];
        	}
        	$dir_md5[$key] = md5(implode(' ',$dir_md5_list));
        }
        
        
        $result =$array;
        $result['attr']['md5'] = md5(implode(' ',$dir_md5));
        foreach($dir_md5 as $key=>$value){
        	$dir_name = explode(DIRECTORY_SEPARATOR,$key);
        	unset($dir_name[0]);
			$cur = &$result;
			foreach ($dir_name as $key=>$v) {
					$cur[$v]['attr']['md5'] = md5($cur[$v]['attr']['md5'].$value);
                    $cur = & $cur[$v];
			}
        }
        return $result;
    }
    
   
    private static function scandir($pPath){
        $dh  = opendir($pPath);
        $files = array();
        while (false !== ($filename = readdir($dh)))
        {
            $files[] = $filename;
        }
        sort($files);
        closedir($dh);

        return $files;
    }
}